package com.bigfans.orderservice.service.impl;

import com.bigfans.framework.Applications;
import com.bigfans.framework.CurrentUser;
import com.bigfans.framework.dao.BaseServiceImpl;
import com.bigfans.framework.event.ApplicationEventBus;
import com.bigfans.framework.model.PageBean;
import com.bigfans.framework.model.PageContext;
import com.bigfans.framework.utils.CollectionUtils;
import com.bigfans.framework.utils.DateUtils;
import com.bigfans.model.event.order.OrderCanceledEvent;
import com.bigfans.model.event.order.OrderCreatedEvent;
import com.bigfans.model.event.order.OrderExpiredItemTakenBackToCartEvent;
import com.bigfans.orderservice.api.clients.CatalogServiceClient;
import com.bigfans.orderservice.api.clients.PricingServiceClient;
import com.bigfans.orderservice.api.clients.UserServiceClient;
import com.bigfans.orderservice.dao.OrderDAO;
import com.bigfans.orderservice.exception.OrderCancelException;
import com.bigfans.orderservice.model.Invoice;
import com.bigfans.orderservice.model.Order;
import com.bigfans.orderservice.model.OrderItem;
import com.bigfans.orderservice.service.InvoiceService;
import com.bigfans.orderservice.service.OrderItemService;
import com.bigfans.orderservice.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * @author lichong 2014年12月5日上午11:24:36
 * @Description:订单服务实现类
 */
@Service(OrderServiceImpl.BEAN_NAME)
public class OrderServiceImpl extends BaseServiceImpl<Order> implements OrderService {

    public static final String BEAN_NAME = "orderService";

    @Autowired
    private OrderItemService orderItemService;
    @Autowired
    private InvoiceService invoiceService;

    @Autowired
    private CatalogServiceClient catalogServiceClient;
    @Autowired
    private UserServiceClient userServiceClient;
    @Autowired
    private PricingServiceClient pricingServiceClient;
    @Autowired
    private ApplicationEventBus applicationEventBus;

    private OrderDAO orderDAO;

    @Autowired
    public OrderServiceImpl(OrderDAO orderDAO) {
        super(orderDAO);
        this.orderDAO = orderDAO;
    }

    @Override
    @Transactional
    public void create(Order order, List<OrderItem> items, Invoice invoice) throws Exception {
        // 创建order
        // 订单流水号(当前时间+用户ID)
        Date createDate = new Date();
        order.setCreateDate(createDate);
        String orderSn = DateUtils.toStringYMDHMS(createDate);
        order.setSn(orderSn + order.getUserId());
        Integer totalQuantity = 0;
        for (OrderItem item : items) {
            totalQuantity += item.getQuantity();
        }
        order.setProdTotalQuantity(totalQuantity);
        super.create(order);

        String orderId = order.getId();

        // 扣库存
        Map<String, Integer> prodQuantityMap = items.stream().collect(Collectors.toMap(OrderItem::getProdId, OrderItem::getQuantity));
        List<CompletableFuture<?>> futures = new ArrayList<>();
        CurrentUser currentUser = Applications.getCurrentUser();
        CompletableFuture<String> stockFuture = catalogServiceClient.orderStockOut(currentUser , orderId, prodQuantityMap);
        futures.add(stockFuture);

        // 消费掉优惠劵 , 积分 , 余额
        if (order.getUsedCouponId() != null || order.getUsedPoint() != null || order.getUsedBalance() != null) {
            CompletableFuture<Void> couponFuture = userServiceClient.useProperty(currentUser , orderId, order.getUsedCouponId(), order.getUsedPoint(), order.getUsedBalance());
            futures.add(couponFuture);
        }

        // 创建orderItem
        for (OrderItem orderItem : items) {
            orderItem.setUserId(order.getUserId());
            orderItem.setOrderId(orderId);
        }
        orderItemService.batchCreate(items);

        // 保存发票信息
        if (invoice != null) {
            invoice.setOrderId(orderId);
            invoiceService.create(invoice);
        }
        // 等待服务调用完成
        CompletableFuture.allOf(futures.stream().toArray(CompletableFuture[]::new)).join();
        applicationEventBus.publishEvent(new OrderCreatedEvent(orderId));
    }

    @Override
    @Transactional
    public int cancel(String userId, String orderId) throws Exception {
        Order order = orderDAO.getByUser(userId, orderId);
        if (!order.getStatus().equals(Order.STATUS_UNPAID)) {
            throw new OrderCancelException("取消订单失败,订单状态已支付.");
        }
        int count = orderDAO.updateStatus(orderId, Order.STATUS_CANCELED);
        applicationEventBus.publishEvent(new OrderCanceledEvent(orderId));
        return count;
    }

    @Override
    @Transactional(readOnly = true)
    public PageBean<Order> pageByUser(String userId, Long start, Long pagesize) throws Exception {
        List<Order> myorders = orderDAO.listByUser(userId, start, pagesize, true);
        Map<String, Integer> expiredItems = new HashMap<>();
        for (Order mo : myorders) {
            List<OrderItem> moitems = orderItemService.listByUserOrder(userId, mo.getId());
            // 如果订单已经过了支付期限，那么将订单中的信息重新放入到购物车中
            if (mo.getHasExpired()) {
                for (OrderItem moitem : moitems) {
                    expiredItems.put(moitem.getProdId(), moitem.getQuantity());
                }
            } else {
                mo.setItems(moitems);
            }
        }
        if (CollectionUtils.isNotEmpty(expiredItems)) {
            applicationEventBus.publishEvent(new OrderExpiredItemTakenBackToCartEvent(expiredItems));
        }
        PageBean<Order> orderPage = new PageBean<Order>(myorders, PageContext.getDataCount());
        return orderPage;
    }

    @Override
    @Transactional(readOnly = true)
    public Order getOrderByUser(String userId, String orderId) throws Exception {
        return orderDAO.getByUser(userId, orderId);
    }

    @Override
    @Transactional
    public int updateStatusToPaid(String orderId) throws Exception {
        return orderDAO.updateStatus(orderId, Order.STATUS_PAID);
    }
}
